﻿using System;
using System.Text;
using System.Web;
using System.Configuration;
using System.Web.Security;

namespace QuickDev.SSO
{
    /// <summary>
    /// The Single sign-on engine
    /// </summary>
    public class SsoEngine
    {
        #region Variable

        /// <summary>
        /// SsoEngine
        /// </summary>
        static SsoEngine()
        {
            Utilities.WriteSession( Constants.SeedSession , NextSeed() );
        }

        #endregion

        #region Public Properties

        /// <summary>
        /// IsAuthenticated
        /// </summary>
        public static bool IsAuthenticated
        {
            get { return GetAuthenticatedTicket() != null; }
        }

        /// <summary>
        /// IsAuthenticating
        /// </summary>
        public static bool IsAuthenticating
        {
            get { return !string.IsNullOrEmpty( Request[Constants.ResponseToken] ); }
        }

        /// <summary>
        /// AuthenticateRequest
        /// </summary>
        public static RequestToken AuthenticateRequest
        {
            get
            {
                string tokenText = Request[Constants.RequestToken];
                if ( string.IsNullOrEmpty( tokenText ) )
                    return null;
                RequestToken token;
                if ( !RequestToken.TryParse( tokenText , out token ) )
                    return null;
                return token;
            }
        }

        #endregion

        #region Protected Method

        /// <summary>
        /// Response
        /// </summary>
        protected static HttpResponse Response
        {
            get { return HttpContext.Current.Response; }
        }

        /// <summary>
        /// Request
        /// </summary>
        protected static HttpRequest Request
        {
            get { return HttpContext.Current.Request; }
        }

        #endregion

        #region Public Methods

        /// <summary>
        /// Authenticate
        /// </summary>
        /// <param name="token">token</param>
        /// <returns>bool successful</returns>
        public static bool Authenticate( out ResponseToken token )
        {
            string tokenText = Request[Constants.ResponseToken];
            if ( !ResponseToken.TryParse( tokenText , out token ) )
                return false;
            if ( token.Seed != Utilities.GetSession( Constants.SeedSession , 0 ) as String )
                return false;
            SsoTicket ticket = new SsoTicket();
            ticket.UserId = token.UserId;
            ticket.UserName = token.UserName;
            ticket.Roles = token.Roles;
            ticket.TimeStamp = token.TimeStamp;
            ticket.ExpireDuration = token.ExpireDuration;
            ResetCookie( Constants.TokenCookieName , ticket.Encode() );
            return true;
        }

        /// <summary>
        /// GetAuthenticatedTicket
        /// </summary>
        /// <returns>SsoTicket</returns>
        public static SsoTicket GetAuthenticatedTicket()
        {
            HttpCookie cookie = Request.Cookies[Constants.TokenCookieName];
            if ( cookie == null )
                return null;
            string cookieText = cookie.Value;
            if ( string.IsNullOrEmpty( cookieText ) )
                return null;
            SsoTicket ticket;
            if ( !SsoTicket.TryParse( cookieText , out ticket ) )
                return null;
            return ticket;
        }

        /// <summary>
        /// RedirectToLogon
        /// </summary>
        public static void RedirectToLogon()
        {
            string returnUrl = HttpContext.Current.Request.Url.ToString();
            RequestToken token = new RequestToken( returnUrl , DateTime.Now , NextSeed() );
            string authUrl = ConfigurationManager.AppSettings[Constants.AuthUrl];
            string redirectUrl = string.Format( "{0}?{1}={2}" , authUrl , Constants.RequestToken , token.Encode() );
            Response.Redirect( redirectUrl );
        }

        /// <summary>
        /// RedirectToApp
        /// </summary>
        /// <param name="returnUrl">returnUrl</param>
        /// <param name="token">token</param>
        public static void RedirectToApp( string returnUrl , ResponseToken token )
        {
            char appendChar = '?';
            if ( returnUrl.IndexOf( '?' ) != -1 )
                appendChar = '&';
            string redirectUrl = string.Format( "{0}{1}{2}={3}" , returnUrl , appendChar , Constants.ResponseToken , token.Encode() );
            Response.Redirect( redirectUrl );
        }

        /// <summary>
        /// SignOut
        /// </summary>
        public static void SignOut()
        {
            HttpContext.Current.Session.Clear();
            HttpContext.Current.Session.Abandon();
            ResetCookie( Constants.TokenCookieName , string.Empty );

            string ssoUrl = ConfigurationManager.AppSettings[Constants.SSOUrl];
            string redirectUrl = string.Format( "{0}?{1}={2}" , ssoUrl , Constants.LogoutUrl , Utilities.GetSiteRoot() );
            Response.Redirect( redirectUrl );
        }

        #endregion

        #region Private Methods

        /// <summary>
        /// ResetCookie
        /// </summary>
        /// <param name="cookieName">cookieName</param>
        /// <param name="cookieValue">cookieValue</param>
        private static void ResetCookie( string cookieName , string cookieValue )
        {
            HttpContext.Current.Response.Cookies[cookieName].Value = cookieValue;
            Response.Cookies.Remove( cookieName );
        }

        /// <summary>
        /// NextSeed
        /// </summary>
        /// <returns>NextSeed</returns>
        private static string NextSeed()
        {
            Random random = new Random( ( int )DateTime.Now.Ticks );
            StringBuilder sb = new StringBuilder();
            for ( int i = 0 ; i < 32 ; i++ )
            {
                int idx = random.Next( 0 , Constants.SeedChars.Length );
                sb.Append( Constants.SeedChars.Substring( idx , 1 ) );
            }
            sb.Append( Guid.NewGuid().ToString() );

            Utilities.WriteSession( Constants.SeedSession , SsoCipher.Hash( sb.ToString() ) );

            return SsoCipher.Hash( sb.ToString() );
        }

        #endregion
    }
}
